/*
 * vim:expandtab:shiftwidth=8:tabstop=8:
 *
 * Copyright CEA/DAM/DIF  (2011)
 * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
 *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
 *
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * ---------------------------------------
 */

/**
 * \file    9p_write.c
 * \brief   9P version
 *
 * 9p_write.c : _9P_interpretor, request WRITE
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef _SOLARIS
#include "solaris_port.h"
#endif

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include "nfs_core.h"
#include "log.h"
#include "cache_inode.h"
#include "fsal.h"
#include "9p.h"

int _9p_write( _9p_request_data_t * preq9p, 
              void  * pworker_data,
              u32 * plenout, 
              char * preply)
{
  char * cursor = preq9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE ;

  u16 * msgtag = NULL ;
  u32 * fid    = NULL ;
  u64 * offset = NULL ;
  u32 * count  = NULL ;

  u32 outcount = 0 ;

  _9p_fid_t * pfid = NULL ;

  fsal_size_t size;
  fsal_size_t written_size = 0;
  fsal_boolean_t eof_met;
  cache_inode_status_t cache_status = CACHE_INODE_SUCCESS;
  //uint64_t stable_flag = CACHE_INODE_SAFE_WRITE_TO_FS;
  uint64_t stable_flag = CACHE_INODE_UNSAFE_WRITE_TO_FS_BUFFER;

  caddr_t databuffer = NULL ;

  fsal_status_t fsal_status ; 
  char xattrval[XATTR_BUFFERSIZE] ;

  /* Get data */
  _9p_getptr( cursor, msgtag, u16 ) ; 
  _9p_getptr( cursor, fid,    u32 ) ; 
  _9p_getptr( cursor, offset, u64 ) ; 
  _9p_getptr( cursor, count,  u32 ) ; 
  
  databuffer = cursor ;
  
  LogDebug( COMPONENT_9P, "TWRITE: tag=%u fid=%u offset=%llu count=%u",
            (u32)*msgtag, *fid, (unsigned long long)*offset, *count  ) ;

  if( *fid >= _9P_FID_PER_CONN )
    return _9p_rerror( preq9p, msgtag, ERANGE, plenout, preply ) ;

  pfid = &preq9p->pconn->fids[*fid] ;

  /* Do the job */
  size = *count ;

  if( pfid->specdata.xattr.xattr_content != NULL )
   { 
     snprintf( xattrval, XATTR_BUFFERSIZE, "%.*s", *count, databuffer ) ;

     _9p_chomp_attr_value( xattrval, strlen( xattrval ) ) ;

     fsal_status = FSAL_SetXAttrValueById( &pfid->pentry->handle,
                                           pfid->specdata.xattr.xattr_id,
                                           &pfid->fsal_op_context,
                                           xattrval,
                                           size+1 ) ;
     if(FSAL_IS_ERROR(fsal_status))
       return _9p_rerror( preq9p, msgtag, _9p_tools_errno( cache_inode_error_convert(fsal_status) ), plenout, preply ) ;

      /* Cache the value */
      memcpy( pfid->specdata.xattr.xattr_content, xattrval, size ) ;

      outcount = *count ;
   }
  else
   {
     if(cache_inode_rdwr( pfid->pentry,
                          CACHE_INODE_WRITE,
                          *offset,
                          size,
                          &written_size,
                          databuffer,
                          &eof_met,
                          &pfid->fsal_op_context,
                          stable_flag,
                          &cache_status ) != CACHE_INODE_SUCCESS )
        return _9p_rerror( preq9p, msgtag, _9p_tools_errno( cache_status), plenout, preply ) ;

      outcount = (u32)written_size ;
    }

  /* Build the reply */
  _9p_setinitptr( cursor, preply, _9P_RWRITE ) ;
  _9p_setptr( cursor, msgtag, u16 ) ;

  _9p_setvalue( cursor, outcount, u32 ) ;

  _9p_setendptr( cursor, preply ) ;
  _9p_checkbound( cursor, preply, plenout ) ;

  LogDebug( COMPONENT_9P, "RWRITE: tag=%u fid=%u offset=%llu input count=%u output count=%u",
            (u32)*msgtag, *fid , (unsigned long long)*offset, *count, outcount ) ;

  return 1 ;
}

